package tech.yixiyun.framework.kuafu.context;

import tech.yixiyun.framework.kuafu.bean.BeanContext;
import tech.yixiyun.framework.kuafu.bean.BeanDefinition;
import tech.yixiyun.framework.kuafu.bean.BeanMode;
import tech.yixiyun.framework.kuafu.boot.ClassScanner;
import tech.yixiyun.framework.kuafu.boot.IBootPreHandler;
import tech.yixiyun.framework.kuafu.boot.bootedhandler.BootedHandlerContext;
import tech.yixiyun.framework.kuafu.boot.bootedhandler.IBootedHandler;
import tech.yixiyun.framework.kuafu.boot.server.MainContextFilter;
import tech.yixiyun.framework.kuafu.boot.server.ServerException;
import tech.yixiyun.framework.kuafu.component.ComponentRegistry;
import tech.yixiyun.framework.kuafu.config.AppConfig;
import tech.yixiyun.framework.kuafu.config.AppConfigException;
import tech.yixiyun.framework.kuafu.config.ConfigKey;
import tech.yixiyun.framework.kuafu.config.NotConfigException;
import tech.yixiyun.framework.kuafu.controller.request.UploadHelper;
import tech.yixiyun.framework.kuafu.kits.StringKit;
import tech.yixiyun.framework.kuafu.kits.TaskKit;
import tech.yixiyun.framework.kuafu.log.LOGGER;
import tech.yixiyun.framework.kuafu.view.freemarker.KuafuFreemarkerServlet;
import org.apache.catalina.servlets.DefaultServlet;

import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import java.util.EnumSet;
import java.util.List;

/**
 * 应用上下文
 */
public class ApplicationContext {

    private static ServletContext servletContext;

    /**
     * 获取Servlet容器的ServletContext
     * @return
     */
    public static ServletContext getServletContext() {
        return servletContext;
    }

    /**
     * 设置ServletContext实例，这个方法只能被框架调用
     * @param context
     */
    public static void setServletContext(ServletContext context) {
        if (servletContext == null) {
            servletContext = context;
            servletContext.setAttribute("contextPath", servletContext.getContextPath());
        }
    }

    /**
     * 获取Bean实例
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return BeanContext.getBean(clazz);
    }

    /**
     * 是否已初始化
     */
    private static  boolean hasInit = false;

    /**
     * 初始化应用,Servlet容器启动时会调用该方法
     * @param sc
     */
    public static void init(ServletContext sc) {
        if (hasInit) return;
        hasInit = true;

        try {
            setServletContext(sc);

            doPreparedWork();

            boot();
        } catch (Exception e) {
            LOGGER.error(e);
            throw new ServerException(e);
        }


    }



    /**
     * 执行启动前的准备工作
     */
    private static void doPreparedWork() {
        LOGGER.infoTitle("^^^ 应用开始执行启动前的准备工作 ^^^ (需要配置PreparedHandler实现类)");
        String className = AppConfig.getAsString(ConfigKey.BOOT_PRE_HANDLER);
        if (StringKit.isNotBlank(className)) {
            try {
                Class clazz = Class.forName(className, false, getServletContext().getClassLoader());
                LOGGER.info("执行 " + clazz.getName() +".handle()");
                IBootPreHandler handler = (IBootPreHandler) clazz.newInstance();
                handler.handle();
            } catch (ClassNotFoundException e) {
                throw new AppConfigException("配置项【"+ConfigKey.BOOT_PRE_HANDLER +"】 的值无效", e);
            } catch (Exception e) {
                if (e instanceof RuntimeException) {
                    throw (RuntimeException)e;
                }
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 执行启动阶段要干的事
     */
    private static void boot() {
        LOGGER.infoTitle("^^^ 应用开始启动 ^^^");
        //扫描并初始化Bean
        scanAndInitBeans();


        //调用启动后处理器
        callBootedHandlers();

        //注册filter
        registerFilter();

        //注册Servlet
        registerServlet();

        //注册websocket
        registerWebSocket();

        //注册上传的临时文件销毁器，commons-fileupload提供的
        UploadHelper.registerUploadCleaner();

    }



    /**
     * 扫描并初始化Bean
     */
    private static void scanAndInitBeans() {

        //扫描所有的类
        List<String> classes = new ClassScanner().scan();

        LOGGER.infoTitle("启动：解析扫描到的类");
        //将类交给解析器处理
        for (String className : classes) {
            ComponentRegistry.register(className);
        }
        //需要实例化的，开始实例化
        BeanDefinition[] definitions = BeanContext.getAllBeanDefinitions();
        for (BeanDefinition definition : definitions) {
            if (definition.getInstance() == null && definition.getIsLazyInit() == false && definition.getBeanMode() == BeanMode.SINGLETON) {
                TaskKit.execute(() -> {
                    BeanContext.getBean(definition.getOriginalClass());
                });
            }
        }
    }



    /**
     * 调用启动后处理器
     */
    private static void callBootedHandlers() {
        List<Class<? extends IBootedHandler>> handlerClasses = BootedHandlerContext.getHandlerClasses();
        LOGGER.infoTitle("启动：执行启动回调器");
        for (Class<? extends IBootedHandler> handlerClass : handlerClasses) {
            IBootedHandler handler = BeanContext.getBean(handlerClass);
            handler.handle();
        }
    }

    /**
     *
     * 注册filter
     */
    private static void registerFilter() {
        List<String> includes = AppConfig.getAsStringList(ConfigKey.REQUEST_RESOURCES_INCLUDE);
        if (includes.isEmpty()) {
            throw new NotConfigException(ConfigKey.REQUEST_RESOURCES_INCLUDE, "未配置框架可处理的请求路径");
        }
        LOGGER.infoTitle("启动：注册ServletFilter");
        FilterRegistration.Dynamic dynamic = ApplicationContext.getServletContext().addFilter("mainContextFilter", MainContextFilter.class);
        dynamic.addMappingForUrlPatterns(
                EnumSet.of(DispatcherType.REQUEST, DispatcherType.INCLUDE, DispatcherType.FORWARD, DispatcherType.ERROR)
                , true, includes.toArray(new String[0]));



    }

    /**
     * 注册Servlet
     */
    private static void registerServlet() {
        LOGGER.infoTitle("启动：注册特殊Servlet");
        //注册一个用于解析freemarker模板的Servlet
        ServletRegistration.Dynamic dynamic = ApplicationContext.getServletContext().addServlet("freemarker", KuafuFreemarkerServlet.class);
        dynamic.setInitParameters(AppConfig.getAsStringMap(ConfigKey.FREEMARKER_CONFIG));
        dynamic.addMapping("*.ftl");
        dynamic.setLoadOnStartup(1);

        //注册默认Servlet
        dynamic = ApplicationContext.getServletContext().addServlet("default", DefaultServlet.class);
        dynamic.setInitParameter("debug","0");
        dynamic.setInitParameter("listings","false");
        dynamic.setInitParameter("fileEncoding", "utf-8");
        dynamic.addMapping("/");
        dynamic.setLoadOnStartup(1);


    }

    /**
     * 注册websocket
     */
    private static void registerWebSocket() {
//        WsServerContainer wsc = (WsServerContainer) getServletContext().getAttribute(Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);
//        if (wsc != null) {
//            try {
//                wsc.addEndpoint(MonitorSocket.class);
//                LOGGER.trace("注册websocket：{}", MonitorSocket.class);
//            } catch (DeploymentException e) {
//                e.printStackTrace();
//            }
//        }
    }


}
